/**
 * Configuration file for Torchd JavaScript Framework
 *
 * @package Torchd Framework
 * @subpackage System
 * @author Jason Raede <jason@torchdm.com>
 */
if(!torchd) var torchd = {};
if(!torchd.system) torchd.system = {};
torchd.system.config = {
	/**
	 * How to handle errors.
	 *
	 * Values can be: 
	 * 		'alert' - Displays a JS alert for each error
	 *		'console' - Shows the error in the console
	 */
	errorHandling:'console'
};
/**
 * Error handler for the Torchd JavaScript Framework
 *
 * @param string method		The method's name in dot notation starting from after "torchd"
 * @param string error		The error to report
 * @package Torchd Framework
 * @subpackage System
 * @author Jason Raede <jason@torchdm.com>
 */
torchd.system.error = function(method, error) {
	var text = 'Torchd Error: (' + method + ') ' + error;
	switch(torchd.system.config.errorHandling) {
		case 'console':
			console.log(text);
			break;
		case 'alert':
			alert(text);
			break;
	}
};
/**
 * Torchd UI Conduit
 *
 * Manages communication between all components of torchd.ui
 *
 * @package Torchd Framework
 * @subpackage UI
 * @author Jason Raede <jason@torchdm.com>
 */
torchd.ui = {
	/** 
	 * Associative array (object?) of open dialog windows
	 */
	openDialogs:{},
	
	/**
	 * Array of menus (typically only one, but we might make varying types in the future)
	 */
	menus:[],
	
	/**
	 * The mask for modal display
	 */
	mask:false,
	
	/**
	 * Display dialogs and windows with a mask in the background?
	 */
	modalDisplay:true,
	
	/** 
	 * Maximum # of dialogs allowed to be open at once.
	 * Leave 0 for no limit
	 *
	 * Note that this is set in the init method so you
	 * don't need to touch it here. This is just default.
	 */
	maxOpenDialogs:0,
	
	/**
	 * Sets the editable parameters (modalDisplay & maxOpenDialogs)
	 */
	setParameters:function(parameters) {
		if(parameters.maxOpenDialogs) this.maxOpenDialogs = parameters.maxOpenDialogs;
		if(typeof parameters.modalDisplay != 'undefined') this.modalDisplay = parameters.modalDisplay;
	},
	
	/**
	 * Creates a dialog window with the parameters specified
	 * 
	 * Available parameters (# is required):
	 *		#{String} title				Title of the dialog window. Shows up in its header
	 *									as well as in internal pointing. 
	 *
	 *		{String} type				Type of dialog. Options are "alert", "confirm", "prompt",
	 *									or "form". Implementations of these are as you would expect.
	 *
	 *		{Object/String} message		Message to display underneath the header. Can be a string,
	 *									or an object with properties for type (used in assigning a
	 *									class to the paragraph object) and message.
	 *
	 *		{Function} onSubmit			Callback function for after the user clicks on "submit" or
	 *									"OK". Data passed to the function depends on the type of form.
	 *									For prompt or form types, the form data is passed as an array
	 *									of objects with name/value pairs for the form elements. For
	 *									confirm, this function is called with no arguments. This is not
	 *									called on alerts.
	 *
	 *		{Function} onCancel			Callback function for after the user clicks on "cancel". This
	 *									function takes no arguments.
	 *
	 *		{Array} formElements		Array of form element definitions to get passed to the 
	 *									torchd.form and torchd.form.field objects. See documentation
	 *									for those objects for valid form element definitions.
	 *								
	 *
	 * @param object params		Object with all parameters. See
	 *							online documentation for more info
	 */
	showDialog:function(params) {
		if(this.modalDisplay) {
			if(!Object.keys(this.openDialogs).length) {
				this.mask = this.createElement('div').hide().addClass('torchd-ui-mask').appendTo('body').fadeIn(100);
			}
		}
		if(!params) {
			torchd.system.error('ui.showDialog', 'You must provide parameters to open a dialog window.');
			return;
		}
	 	if(!params.title) {
	 		torchd.system.error('ui.showDialog', 'Parameters must include a title property for internal pointing.');
	 		return;
	 	}
		if(this.openDialogs[params.title]) {
			this.openDialogs[params.title].close();
			delete this.openDialogs[params.title];
		}
	 	this.openDialogs[params.title] = new torchd.ui.dialog(params);
	 },
	 
	 /**
	  * Creates a generic element using jQuery.
	  *
	  * Used in order to add the torchd-ui class to all elements.
	  *
	  * @param string elementType		The element type
	  */
	 createElement:function(elementType) {
	 	return $('<' + elementType + '/>').addClass('torchd-ui');
	 },

	 /**
	  * Closes the dialog window with this specific title
	  *
	  * @param string title		The title of the window
	  */
	 closeDialog:function(title) {
	 	this.openDialogs[title].close();
	 	if(this.modalDisplay) {
			if(Object.keys(this.openDialogs).length <= 1) {
				$('.torchd-ui-mask').remove();
			}
		}
	 },
	 
	 closeAllDialogs:function() {
	 	for(var title in this.openDialogs) {
	 		this.openDialogs[title].close();
	 	}
	 	$('.torchd-ui-mask').remove();
	 },
	 
	 /**
	  * Event handler when a dialog is closed. 
	  *
	  * Check if there are any other dialogs still open; if not,
	  * then trash the mask as well.
	  *
	  * @param string title		The title of the dialog, for internal pointing.
	  */
	 dialogClosed:function(title) {
	 	var self = this;
	 	delete this.openDialogs[title];
	 	if(this.modalDisplay && !Object.keys(this.openDialogs).length) {
	 		// Fade out the mask
	 		if(this.mask) {
		 		this.mask.fadeOut(200, function() {
		 			$(this).remove();
		 			self.mask = false;
		 		});
		 	}
	 	}
	 },
	 
	 /**
	  * Display a menu bar with the given definition.
	  *
	  * See menu bar object for detailed description of menu bar definition object
	  *
	  * @param {Object} definition
	  */
	 showMenu:function(definition) {
	 	this.menus.push(new this.menuBar(definition));
	 	var self = this;
	 	$(document).click(function() {
	 		for(var i=0;i<self.menus.length;i++) {
	 			if(self.menus[i].openMenu) {
		 			self.menus[i].openMenu.hide();
					self.menus[i].openMenu.parent('li').removeClass('selected');
					delete self.menus[i].openMenu;
				}
	 		}
	 	});
	 },
	 
	 addTooltip:function(element, params) {
	 	new this.tooltip(element, params);
	 }
	 
};
/**
 * Torchd UI Dialog Window
 *
 * Opens a scalable dialog window
 *
 * @package Torchd Framework
 * @subpackage UI
 * @author Jason Raede <jason@torchdm.com>
 */

torchd.ui.dialog = function(params) {
	this.init(params);
};

/** STATIC METHODS */
torchd.ui.dialog.sizes = {
	small:['400', '200'],
	medium:['500', '250'],
	large:['700', '350'],
	full:['900', '450']
};

torchd.ui.dialog.parseSize = function(size) {
	if(['small', 'medium', 'large', 'full'].indexOf(size)) {
		return this.sizes[size];
	}
	else if(typeof size === 'array' && size.length == 2) {
    	return size;
    }
    else {
    	return false;
    }
};

torchd.ui.dialog.defaultParams = {
	title:'',
	mode:'default',
	modal:true,
	type:'alert'
};

/** INSTANCE METHODS */

/**
 * Initialize the dialog with the parameters
 *
 * @param object params
 */
torchd.ui.dialog.prototype.init = function(params) {
	var self = this;
	
	var params = $.extend({}, torchd.ui.dialog.defaultParams, params);
	this.title = params.title;
	// Create the element
	this.window = torchd.ui.createElement('div').addClass('torchd-ui-dialog');
	
	
	
	
	
	// Title
	torchd.ui.createElement('h3').addClass('torchd-ui-widget-header').html(params.title).appendTo(this.window);
	
	// Inner content
	this.content = $('<div/>').addClass('torchd-ui-inner').appendTo(this.window);
	if(params.content) this.content.html(params.content);
	
	if(params.buttons) this.buttons = $('<div/>').addClass('torchd-ui-dialog-buttons').appendTo(this.window);
	// Message?
	if(params.message) {
		if(typeof params.message == 'object') {
			$('<p/>').addClass('torchd-ui-message-' + params.message.type).html(params.message.text).appendTo(this.content);
		}
		else if(typeof params.message == 'string') {
			$('<p/>').addClass('torchd-ui-message-normal').html(params.message).appendTo(this.content);
		}
		else {
			torchd.system.error('ui.dialog.init', 'Message type must be an object or a string.');
		}
		
	}
	this.window.hide().appendTo('body');
	// Type
	switch(params.type) {
		case 'ajax':
			$.ajax({
				url:params.ajaxUrl,
				type:'GET',
				dataType:'json',
				beforeSend:function() {
					self.content.html('<div class="torchd-ui-ajax-loader"></div>');
					self.window.css({
						marginTop:'-'+(parseInt(self.window.realHeight())/2).toString()+'px',
						marginLeft:'-'+(parseInt(self.window.realWidth())/2).toString()+'px',
					});
				},
				success:function(response) {
					self.content.html(response.content);
					if(params.buttons) {
						for(var i=0;i<params.buttons.length;i++) {
							if(!response['hide_'+params.buttons[i].id]) {
								var okButton = $('<button/>').attr('id', params.buttons[i].id).data('dialog', self).html(params.buttons[i].title).click(params.buttons[i].clickAction).appendTo(self.buttons);
							}
						}
					}
					self.window.css({
						marginTop:'-'+(parseInt(self.window.realHeight())/2).toString()+'px',
						marginLeft:'-'+(parseInt(self.window.realWidth())/2).toString()+'px',
					});
					
				}
			});
			
			
			break;
		case 'alert':
		default:
			var okButton = $('<button/>').data('dialog', this).html('OK').click(function() {
				$(this).data('dialog').close();
			}).appendTo(this.content);
			break;
		case 'ajax-loader':
			this.content.html('<div class="torchd-ui-ajax-loader"></div>');
			break;
		case 'confirm':
			var cancelButton = $('<button/>').data('dialog', this).html('Cancel').click(function() {
				if(params.onCancel) params.onCancel();
				$(this).data('dialog').close();
			}).appendTo(this.content);
			var okButton = $('<button/>').data('dialog', this).html('OK').click(function() {
				if(params.onSubmit) params.onSubmit($(this).data('dialog').content);
				$(this).data('dialog').close();
			}).appendTo(this.content);
			break;
		case 'form':
			var form = new torchd.form(params.formElements);
			form.insert(this.content);
			var cancelButton = $('<button/>').data('dialog', this).html('Cancel').click(function() {
				if(params.onCancel) params.onCancel();
				$(this).data('dialog').close();
			}).appendTo(this.content);
			
			var submitButton = $('<button/>').data('dialog', this).html('Submit').click(function() {
				if(params.onSubmit) {
					params.onSubmit(form.getData());
				}
				$(this).data('dialog').close();				
				
			}).appendTo(this.content);
			break;
		case 'custom':
			params.construct(this);
			var cancelButton = $('<button/>').data('dialog', this).html('Close').click(function() {
				if(params.onCancel) params.onCancel();
				$(this).data('dialog').close();
			}).appendTo(this.content);
			break;
		case 'prompt':
			// One field, two buttons (cancel + submit)
			if(params.defaultValue) {
				var field = new torchd.form.field({
					name:'',
					type:'text',
					data:'varchar',
					fields:[{
						name:'prompt',
						defaultValue:params.defaultValue
					}]
				});
			}
			else {
				var field = new torchd.form.field({
					name:'',
					type:'text',
					data:'varchar',
					fields:[{
						name:'prompt'
					}]
				});
			}
			field.appendTo(this.content);
			var cancelButton = $('<button/>').data('dialog', this).html('Cancel').click(function() {
				if(params.onCancel) params.onCancel();
				$(this).data('dialog').close();
			}).appendTo(this.content);
			
			var submitButton = $('<button/>').data('dialog', this).html('Submit').click(function() {
				if(params.onSubmit) {
					params.onSubmit(field.getData());
				}
				$(this).data('dialog').close();				
				
			}).appendTo(this.content);
			break;
	}
	
	// Size
	
	var size = torchd.ui.dialog.parseSize(params.size);
	if(size) {
		this.window.css({
			width:size[0],
			height:size[1],
			marginTop:'-'+(parseInt(size[1])/2).toString()+'px',
			marginLeft:'-'+(parseInt(size[0])/2).toString()+'px'
		});
	}
	else if(params.type != 'ajax'){
		this.window.css({
			marginTop:'-'+(parseInt(this.window.realHeight())/2).toString()+'px',
			marginLeft:'-'+(parseInt(this.window.realWidth())/2).toString()+'px',
		});
	}
	
	this.window.fadeIn(200);
};
/**
 * Display a loading animation in the inner dialog 
 */
torchd.ui.dialog.prototype.ajaxLoader = function() {
	this.content.html('<div class="torchd-ui-ajax-loader"></div>');
	this.window.css({
		marginTop:'-'+(parseInt(this.window.realHeight())/2).toString()+'px',
		marginLeft:'-'+(parseInt(this.window.realWidth())/2).toString()+'px',
	});
};

/**
 * Close the dialog window and release it from memory
 */
torchd.ui.dialog.prototype.close = function() {
	var self = this;
	this.window.fadeOut(100, function() {
		$(this).remove();
		delete self.window;
		delete self.content;
	});
	torchd.ui.dialogClosed(this.title);
};





/**
 * Torchd UI Menu Bar
 *
 * Initializes and displays a menu bar similar to those in desktop operating systems
 *
 * @package Torchd Framework
 * @subpackage UI
 * @author Jason Raede <jason@torchdm.com>
 */
torchd.ui.menuBar = function(menus) {
	this.init(menus);
};

/**
 * Initialize the menu with a menu definition array.
 *
 * Menu definitions are structured as follows:
 *			{
 *				menu_name (e.g. File): [
 *					{
 *						name:'button_name' (e.g. "new"),
 *						display:'button_display' (e.g. "New"),
 *						defaultState:'enabled' or 'disabled',
 *						buttons: Optional array of same structure of this
 *									for multi-level menus
 *						clickAction: function when button is clicked
 *					},
 *					{
 *						// Another button
 *					}
 *				],
 *				another_menu (e.g. Edit): [
 *				
 *				],
 *			}
 */
torchd.ui.menuBar.prototype.init = function(menus) {
	var self = this;
	this.menus = {};
	var menuElement = torchd.ui.createElement('ul').addClass('torchd-ui-menu').prependTo($('body'));
	this.menuElement = menuElement;
	for(var index in menus) {
		var menuInfo = menus[index];
		var button = $('<li/>').addClass('torchd-ui-menu-tab').html(index).click(function(e) {
			e.stopPropagation();
				if(self.openMenu && !self.openMenu.is(':hidden')) {
					if(self.openMenu && !self.openMenu.is(':hidden')) {
						self.openMenu.hide();
						self.openMenu.parent('li').removeClass('selected');
						self.openMenu = false;
					} 
			 		
				} 
				if($(this).children('ul').is(':hidden')) {
					$(this).addClass('selected');
					$(this).children('ul').show();
					self.openMenu = $(this).children('ul');
				}
				else {
					$(this).removeClass('selected');
					$(this).children('ul').hide();
					self.openMenu = false;
				}
			}).mouseover(function() {
				if(self.openMenu) {
					self.openMenu.hide();
					self.openMenu.parent('li').removeClass('selected');
					$(this).addClass('selected').children('ul').show();
					self.openMenu = $(this).children('ul');
				}
			}).appendTo(menuElement);
		if(menuInfo.class) {
			button.addClass(menuInfo.class);
		}
		var menu = $('<ul/>').appendTo(button);
		this.menus[index] = menu;
		this.menus[index].data('children', {});
		// Add all buttons to menu
		
		for(var i=0;i<menuInfo.length;i++) {
			var items = menuInfo[i];
			if(!items.clickAction) items.clickAction = false;
			if(!items.defaultState) items.defaultState = false;
			var html = items.display;
			if(items.buttons) {
				html = items.display + '<span class="torchd-ui-menu-arrow">&rarr;</span>';
			}
			if(!html) html = '';
			
			var li = $('<li/>').html(html).appendTo(menu);
			if(items.class) li.addClass(items.class);
			var children = this.menus[index].data('children');
			children[items.name] = li;
			this.menus[index].data('children', children);
			if(items.buttons) {
				var ul = $('<ul/>').appendTo(li);
				for(var n=0;n<items.buttons.length;n++) {
					
					var buttonInfo = items.buttons[n];
					
					if(buttonInfo.name == 'separator') {
						$('<li/>').addClass('torchd-ui-menu-separator').disableSelection().appendTo(ul);
					}
					else {
						var button = $('<li/>').html(buttonInfo.display).data('state', buttonInfo.defaultState).data('defaultHTML', buttonInfo.display).data('clickAction', buttonInfo.clickAction).click(function(e) {
							e.stopPropagation();
							if($(this).data('state') == 'enabled') {
								var action = $(this).data('clickAction');
								console.log(self.openMenu);
								self.openMenu.hide();
								
								self.openMenu.parent('li').removeClass('selected');
								self.openMenu = false;
								action();
							}
						}).disableSelection().addClass(buttonInfo.defaultState).appendTo(ul);
					}
				}
			}
			else {
				if(items.name == 'separator') {
					li.addClass('torchd-ui-menu-separator').disableSelection();
				}
				else {
					li.addClass(items.defaultState).data('state', items.defaultState).data('clickAction', items.clickAction).append('<span class="torchd-ui-menu-arrow"></span>').click(function(e) {

						e.stopPropagation();
						if($(this).data('state') == 'enabled') {
							console.log(self.openMenu);
							self.openMenu.parent('li').removeClass('selected');
							self.openMenu.css('display', 'none');
							self.openMenu = false;
							var action = $(this).data('clickAction');
							action();
						}
					}).disableSelection();
					
				}
				
			}
			
		}
	}
};

torchd.ui.menuBar.prototype.addMenuItem = function(buttonInfo) {
	var self = this;
	var parent = this.menus[buttonInfo.menu];
	if(buttonInfo.subMenu) {
		var children = parent.data('children');
		var parent = children[buttonInfo.subMenu].children('ul');
	}
	if(buttonInfo.name == 'separator') {
		$('<li/>').addClass('torchd-ui-menu-separator').disableSelection().appendTo(parent);
	}
	else {
		var button = $('<li/>').html(buttonInfo.display).data('state', buttonInfo.defaultState).data('defaultHTML', buttonInfo.display).data('clickAction', buttonInfo.clickAction).click(function(e) {
			e.stopPropagation();
			if($(this).data('state') == 'enabled') {
				var action = $(this).data('clickAction');
				self.openMenu.hide();
				
				self.openMenu.parent('li').removeClass('selected');
				self.openMenu = false;
				action($(this));
			}
		}).disableSelection().addClass(buttonInfo.defaultState).appendTo(parent);
		if(buttonInfo.data) {
			button.data('data', buttonInfo.data);
		}
		if(buttonInfo.class) button.addClass(buttonInfo.class);
	}
	
};
torchd.form = function(params) {
	this.init(params);
};

torchd.form.prototype.getData = function() {
	var data = [];
	for(var i=0;i<this.fields.length;i++) {
		data.push(this.fields[i].getData());
	}
	return data;

};

torchd.form.prototype.init = function(params) {
	var self = this;
	this.params = $.extend({
			buttons:{
				submit:{
					show:false,
					text:'Submit',
					onClick:false
				},
				cancel:{
					show:false,
					text:'Back',
					onClick:false
				}
			},
			message:'',
			formItems:[]
			
		},
		params);
		
	this.buttons = [];
	this.formElements = [];
	this.fields = [];
	this.domElement = $('<form/>');
	if(params.message) {
		this.message = $('<div/>').addClass('torchd-ui-form-message').html(this.params.message).appendTo(this.domElement);
	}
	this.formItems = $('<div/>').addClass('torchd-ui-form-items').appendTo(this.domElement);
	this.buttons = $('<div/>').addClass('torchd-ui-form-buttons').appendTo(this.domElement);
	
	// Go through form items and add them to the form
	for(var n=0;n<this.params.formItems.length;n++) {
		var field = new torchd.form.field(this.params.formItems[n]);
		field.appendTo(this.formItems);
		this.fields.push(field);
	}
	
	// Make buttons
	if(this.params.buttons.submit.show) {
		this.buttons.submit = $('<button/>').html(this.params.buttons.submit.text).click(function(e) {
			e.preventDefault();
			if(self.params.buttons.submit.onClick) {
				self.params.buttons.submit.onClick(self.formItems);
			}
		}).appendTo(this.buttons);
	}
	if(this.params.buttons.cancel.show) {
		this.buttons.cancel = $('<button/>').html(this.params.buttons.cancel.text).click(function(e) {
			e.preventDefault();
			if(self.params.buttons.cancel.onClick) {
				self.params.buttons.cancel.onClick();
			}
		}).appendTo(this.buttons);
	}
	
	// Return key is submit, escape key is cancel
	this.domElement.keydown(function(event) {
		if(event.keyCode == 13) {
			if(this.params.buttons.submit.show) {
				this.buttons.submit.trigger('click');
			}
		}
		else if(event.keyCode == 27) {
			if(this.params.buttons.cancel.show) {
				this.buttons.cancel.trigger('click');
			}
		}
	});
};

torchd.form.prototype.insert = function(element) {
	this.domElement.appendTo(element);
	for(var y=0;y<this.fields.length;y++) {
		this.fields[y].initSpecials();
	}
};
torchd.form.field = function(options) {
	this.init(options);
};

torchd.form.field.prototype.init = function(options) {
	var self = this;
	// Create the dom element based on parameters
	
	// Generate form fields
	this.elements = [];
	for(var i=0;i<options.fields.length;i++) {
		switch(options.fields[i].type) {
			case 'text':
			default:	
				var val = (options.fields[i].defaultValue) ? options.fields[i].defaultValue : '';
				var element = $('<input/>').val(val).attr('type', 'text');
				if(options.fields[i].class) {
					element.addClass(options.fields[i].class);
				}
				if(options.fields[i].label) {
					element.data('label', options.fields[i].label);
				}
				if(options.fields[i].name) {
					element.attr('name', options.fields[i].name);
				}	
				this.elements.push(element);
				break;
			case 'textarea':
				var val = (options.fields[i].defaultValue) ? options.fields[i].defaultValue : '';
				var element = $('<textarea/>').attr('name', options.fields[i].name).html(val);
				this.elements.push(element);
				break;
			case 'radio':
				for(var n=0;n<options.fields[i].options.length;n++) {
					this.elements.push($('<input/>').attr('type', 'radio').data('label', options.fields[i].options[n].label).val(options.fields[i].options[n].value).attr('name', options.fields[i].name));
				}
				break;
			case 'checkbox':
				for(var n=0;n<options.fields[i].options.length;n++) {
					this.elements.push($('<input/>').attr('type', 'checkbox').data('label', options.fields[i].options[n].label).val(options.fields[i].options[n].value).attr('name', options.fields[i].options[n].name));
				}
				break;
			case 'size':
				var amount = $('<input/>').attr('type', 'text').attr('name', options.fields[i].name + '_amount').addClass('torchd-ui-thin');
				var unit = $('<select/>').attr('name', options.fields[i].name + '_unit');
				$('<option/>').val('%').html('%').appendTo(unit);
				$('<option/>').val('px').html('Pixels').appendTo(unit);
				this.elements.push(amount);
				this.elements.push(unit);
				break;
			case 'select':
				var select = $('<select/>').data('label', options.fields[i].label).attr('name', options.fields[i].name);
				for(var n=0;n<options.fields[i].options.length;n++) {
					var option = $('<option/>').html(options.fields[i].options[n].label).val(options.fields[i].options[n].value);
					if(options.fields[i].defaultValue == options.fields[i].options[n].value) {
						option.attr('selected', 'selected');
					}
					select.append(option);
				}
				this.elements.push(select);
				break;
			case 'livesearchMultiple':
				var select = $('<select/>').attr('multiple', 'multiple').data('livesearch', 'multiple').data('label', options.fields[i].label).attr('name', options.fields[i].name);
				for(var n=0;n<options.fields[i].options.length;n++) {
					var option = $('<option/>').html(options.fields[i].options[n].label).val(options.fields[i].options[n].value);
					if(options.fields[i].defaultValue == options.fields[i].options[n].value) {
						option.attr('selected', 'selected');
					}
					select.append(option);
				}
				this.elements.push(select);
				break;
			case 'livesearch':
				var select = $('<select/>').data('livesearch', 'singular').data('label', options.fields[i].label).attr('name', options.fields[i].name);
				for(var n=0;n<options.fields[i].options.length;n++) {
					var option = $('<option/>').html(options.fields[i].options[n].label).val(options.fields[i].options[n].value);
					if(options.fields[i].defaultValue == options.fields[i].options[n].value) {
						option.attr('selected', 'selected');
					}
					select.append(option);
				}
				this.elements.push(select);
				break;
			case 'datetime':
				var val = (options.fields[i].defaultValue) ? options.fields[i].defaultValue : '';
				var element = $('<input/>').attr('type', 'text').attr('readonly', 'readonly').data('datetime', true).attr('name', options.fields[i].name).html(val);
				this.elements.push(element);
				break;
			case 'color':
				var val = (options.fields[i].defaultValue) ? options.fields[i].defaultValue : '';
				var element = $('<input/>').val(val).addClass('colorpicker').attr('type', 'text').data('label', options.fields[i].label).data('colorPicker', true).attr('name', options.fields[i].name);
				
				this.elements.push(element);
				break;
				
			
		}
	}
	// Add them to the fieldset
	this.fieldset = $('<fieldset/>');
	
	if(this.elements.length == 1) {
		if(options.display) {
			$('<legend/>').html(options.display).appendTo(this.fieldset);
		}
		this.fieldset.append(this.elements[0]);
	}
	else if(this.elements.length > 1) {
		if(options.display) {
			$('<legend/>').html(options.display).appendTo(this.fieldset);
		}
		for(var i=0;i<this.elements.length;i++) {
			var grid = $('<div/>').addClass('form-grid').appendTo(this.fieldset);
			if(options.gridClass) {
				grid.addClass(options.gridClass);
			}
			$('<label/>').html(this.elements[i].data('label')).appendTo(grid);
			this.elements[i].appendTo(grid);
		}
		$('<div/>').css('clear', 'both').appendTo(this.fieldset);
	}
	else {
		alert('Error creating form field');
	}
};

/**
 * Get the data for each of the form fields and return it in an array
 *
 * @return array returnArray		Array of form field name/value pairs
 */
torchd.form.field.prototype.getData = function() {
	var returnArray = [];
	for(var i=0;i<this.elements.length; i++) {
		returnArray.push({
			name:this.elements[i].attr('name'),
			val:this.elements[i].val()
		});
	}
	return returnArray;
};

torchd.form.field.prototype.appendTo = function(parent) {
	this.fieldset.appendTo(parent);
	
};

torchd.form.field.prototype.initSpecials = function() {
	var fieldset = this.fieldset;
	// Livesearchify any elements that need it
	this.fieldset.find('select').each(function() {
		if($(this).data('livesearch')) {
			$(this).livesearch($(this).data('livesearch'));
		}
	});
	
	// Datetime picker & color picker
	this.fieldset.find('input').each(function() {
		if($(this).data('datetime')) {
			$(this).datetimepicker();
		}
		else if($(this).data('colorPicker')) {
			var val = $(this).val();
			$(this).jscolor();
			if(val == 'none') {
				$(this).val('').css('background-color', '#3a3a3a').css('color', 'white');
			}
			var none = $('<button/>').html('X').addClass('torchd-ui-colorpicker-button').data('colorPicker', $(this)).click(function(e) {
				e.preventDefault();
				$(this).data('colorPicker').val('').css('background-color', '#3a3a3a').css('color', 'white');
			}).insertAfter($(this));
		}
	});
	
};

torchd.ui.tooltip = function(element, params) {
	this.init(element, params);
};


torchd.ui.tooltip.prototype.init = function(element, setParams) {
	var self = this;
	var params = {};
	this.element = element;
	jQuery.extend(params,setParams);
	this.animate = params.animate;
	this.animateSpeed = params.animateSpeed;
	this.position = params.placement;
	this.tooltip = torchd.ui.createElement('div').hide().addClass('torchd-ui-tooltip').html(params.html).insertAfter(element);
	
	
	
	
	$(document).mouseover(function() {
		self.hide();
	});
	this.tooltip.data('tooltip', this).mouseover(function(e) {
		e.stopPropagation();
	});
	element.data('tooltip', this).mouseover(function(e) {
		e.stopPropagation();
		$(this).data('tooltip').show();
	});
};

torchd.ui.tooltip.prototype.place = function() {
	var element = this.element;
	// Get element position
	var elementPos = element.offset();
	var elementHeight = element.realHeight();
	var elementWidth = element.realWidth();
	var tooltipHeight = this.tooltip.realHeight();
	var tooltipWidth = this.tooltip.realWidth();
	
	if(!this.position) {
		var vert = '';
		var horz = '';
		// Get element position relative to window 
		var scrollLeft = $(window).scrollLeft();
		var scrollTop = $(window).scrollTop();
		var elementCenter = {
			left:elementPos.left + (elementWidth/2) - scrollLeft,
			top:elementPos.top + (elementHeight/2) - scrollTop
		};
		var windowHeight = $(window).height();
		var windowWidth = $(window).width();
		if(elementCenter.left / windowWidth < .15) {
			horz = 'e';
		}
		else if(elementCenter.left / windowWidth < .85) {
			horz = '';
		}
		else {
			horz = 'w';
		}
		
		if(elementCenter.top / windowHeight < .5) {
			vert = 's';
		}
		else {
			vert = 'n';
		}
		
		position = vert + horz;
	}
	else {
		position = this.position;
	}
	
	this.tooltip.removeClass('torchd-ui-tooltip-placement-n').removeClass('torchd-ui-tooltip-placement-nw').removeClass('torchd-ui-tooltip-placement-ne').removeClass('torchd-ui-tooltip-placement-w').removeClass('torchd-ui-tooltip-placement-e').removeClass('torchd-ui-tooltip-placement-s').removeClass('torchd-ui-tooltip-placement-sw').removeClass('torchd-ui-tooltip-placement-se').addClass('torchd-ui-tooltip-placement-' + position);

	switch(position) {
		case 's':
			this.tooltip.css('top', (elementPos.top + elementHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2)) - (tooltipWidth/2)).toString() + 'px');
			break;
		case 'se':
			this.tooltip.css('top', (elementPos.top + elementHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2))).toString() + 'px');
			break;
		case 'sw':
			this.tooltip.css('top', (elementPos.top + elementHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2)) - tooltipWidth).toString() + 'px');
			break;
		case 'w':
			this.tooltip.css('top', ((elementPos.top + elementHeight/2) - (tooltipHeight/2)).toString() + 'px');
			this.tooltip.css('left', (elementPos.left - tooltipWidth).toString() + 'px');
			break;
		case 'e':
			this.tooltip.css('top', ((elementPos.top + elementHeight/2) - (tooltipHeight/2)).toString() + 'px');
			this.tooltip.css('left', (elementPos.left + elementWidth).toString() + 'px');
			break;
		case 'n':
			this.tooltip.css('top', (elementPos.top - tooltipHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2)) - (tooltipWidth/2)).toString() + 'px');
			break;
		case 'ne':
			this.tooltip.css('top', (elementPos.top - tooltipHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2))).toString() + 'px');
			break;
		case 'nw':
			this.tooltip.css('top', (elementPos.top - tooltipHeight).toString() + 'px');
			this.tooltip.css('left', ((elementPos.left + (elementWidth / 2)) - tooltipWidth).toString() + 'px');
			break;
			
			
			
			
	}
};

torchd.ui.tooltip.prototype.show = function() {
	this.place();

	if(this.animate == 'slide') {
		//this.tooltip.show();
		
		var animate = {
			height:tooltipInfo.height,
			width:tooltipInfo.width
		};
		
		var css = {
			height:'0px',
			width:'0px'
		};
		
		
		this.tooltip.css(css).show().animate(animate, this.animateSpeed);
	}
	else if(this.animate == 'fade') {
		this.tooltip.fadeIn(this.animateSpeed);
	}
	else {
		this.tooltip.show();
	}	
};

torchd.ui.tooltip.prototype.hide = function() {

	if(this.animate == 'slide') {
		
		var animate = {
			height:'0px',
			width:'0px'
		};
		switch(this.position) {
			case 'ne':
				animate.top = (parseInt(this.tooltipInfo.top) + parseInt(this.tooltipInfo.realHeight)).toString() + 'px' ;
				break; 
			case 'nw':
				animate.left = this.tooltipInfo.left;
				break;
			case 'se':
				animate.top = top;
				animate.left = this.tooltipInfo.left;
				break;
			case 's':
				animate.top = this.tooltipInfo.top;
				break;
			case 'sw':
				animate.top = this.tooltipInfo.top;
				break;
		}
		
		this.tooltip.animate(animate, this.animateSpeed, function() {
			$(this).hide();
		});
	}
	else if(this.animate == 'fade') {
		this.tooltip.fadeOut(this.animateSpeed);
	}
	else {
		this.tooltip.hide();
	}	
};


$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

(function($){
    $.fn.realWidth = function() {
    	return $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right')) + parseInt($(this).css('border-left-width')) + parseInt($(this).css('border-right-width'));
		
    };
    
    $.fn.realHeight = function() {
    	return $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom')) + parseInt($(this).css('border-bottom-width')) + parseInt($(this).css('border-top-width'));
    };
    
    
   
})(jQuery);
(function($){
    $.fn.torchdTooltip = function(params) {
    	return this.each(function() {
    		torchd.ui.addTooltip($(this), params);
    	});
    };
    
    
   
})(jQuery);